package com.atguigu.spzx.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.cart.client.CartClient;
import com.atguigu.spzx.common.handler.exception.SpzxException;
import com.atguigu.spzx.common.handler.interceptor.SpzxServiceAuthInterceptor;
import com.atguigu.spzx.common.util.consts.SpzxConst;
import com.atguigu.spzx.model.dto.h5.OrderSubmitDto;
import com.atguigu.spzx.model.entity.h5.CartInfo;
import com.atguigu.spzx.model.entity.h5.OrderItem;
import com.atguigu.spzx.model.entity.h5.UserAddress;
import com.atguigu.spzx.model.entity.h5.UserInfo;
import com.atguigu.spzx.model.entity.order.OrderInfo;
import com.atguigu.spzx.model.entity.product.ProductSku;
import com.atguigu.spzx.model.result.Result;
import com.atguigu.spzx.model.result.ResultCodeEnum;
import com.atguigu.spzx.model.vo.h5.TradeVo;
import com.atguigu.spzx.order.mapper.OrderMapper;
import com.atguigu.spzx.order.service.OrderItemService;
import com.atguigu.spzx.order.service.OrderService;
import com.atguigu.spzx.service.product.client.ProductClient;
import com.atguigu.spzx.user.client.UserClient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderInfo> implements OrderService {
    @Resource
    CartClient cartClient;
    @Resource
    ProductClient productClient;
    @Resource
    UserClient userClient;
    @Resource
    OrderItemService orderItemService;
    @Resource
    KafkaTemplate kafkaTemplate;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    //去结算
    @Override
    public TradeVo trade() {
        //1、查询当前用户的购物车中已选中的购物项列表(cart服务管理购物车数据)
        // feign获取到得到结果 如果没有指定泛型，json对象会自动转为LinkedHashMap
        Result<List<CartInfo>> result = cartClient.checkedCartInfos();
        if(result.getCode()!=200){
            throw  new SpzxException(ResultCodeEnum.FAIL , null);
        }
        List<CartInfo> cartInfos = result.getData();
        if(CollectionUtils.isEmpty(cartInfos)){
            //没有选中的购物项
            throw  new SpzxException(ResultCodeEnum.FAIL , null);
        }
        Long token = IdUtil.getSnowflake(1,1).nextId();
        //将token存到redis: redis的大key问题
        stringRedisTemplate.opsForValue().set("spzx:order:"+token.toString(),"1",30, TimeUnit.MINUTES);
        //2、将购物项列表转为 OrderItem列表
        List<OrderItem> orderItems = cartInfos.stream().map(cartInfo -> {
            OrderItem orderItem = new OrderItem();
            orderItem.setOrderId(token);
            orderItem.setSkuNum(cartInfo.getSkuNum());
            orderItem.setSkuName(cartInfo.getSkuName());
            orderItem.setSkuPrice(cartInfo.getCartPrice());
            orderItem.setThumbImg(cartInfo.getImgUrl());
            orderItem.setSkuId(cartInfo.getSkuId());
            return orderItem;
        }).collect(Collectors.toList());

        TradeVo tradeVo = new TradeVo();
        tradeVo.setOrderItemList(orderItems);
        //遍历每一个订单项，计算它的小计金额返回
        //最后对所有小计金额累加 得到总金额
        BigDecimal totalAmount = orderItems.stream().map(orderItem ->
                        orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum()))
                )
                .reduce((a, b) -> a.add(b)) //累加
                .get();//获取累加的结果
        tradeVo.setTotalAmount(totalAmount);

        return tradeVo;
    }
    //保存订单数据：orderInfo  orderItems
   @Transactional(rollbackFor = Exception.class)
    @Override
    public Long submitOrder(OrderSubmitDto orderSubmitDto) {
        UserInfo userInfo = SpzxServiceAuthInterceptor.THREAD_LOCAL.get();
        //1、幂等性保证：用户订单确认页提交的相同的请求只处理一次
        //获取前端提交的token
       List<OrderItem> orderItems = orderSubmitDto.getOrderItemList();
       if(CollectionUtils.isEmpty(orderItems)){//没有购物项
           throw new SpzxException(ResultCodeEnum.FAIL, null);
       }
       Long token = orderItems.get(0).getOrderId();
       //获取redis缓存的token，如果存在删除
       Boolean delete = stringRedisTemplate.delete("spzx:order:" + token.toString());//查询和删除token要保证原子性
       if(!delete){
           //删除失败： 前端没有携带token 或者 redis没有缓存  或者 重复提交的请求
           throw new SpzxException(ResultCodeEnum.ORDER_RESUBMIT_ERROR,null);
       }
       //2、保证订单中商品价格正确、库存足够
        // 验证前端提交的每个orderItem的价格 和skuId对应的商品当前价格是否一样  同时校验要购买的商品数量库存是否足够
        // 远程调用：product 根据skuId查询productSku详情(商品价格、库存)
        orderItems.forEach(orderItem -> {
            Result<ProductSku> skuResult = productClient.getById(orderItem.getSkuId());
            if(skuResult.getCode()!=200){
                //订单项对应的商品查询失败
                throw new SpzxException(ResultCodeEnum.FAIL, null);
            }
            ProductSku sku = skuResult.getData();
            //sku.getSalePrice().equals(orderItem.getSkuPrice())
            if(sku.getSalePrice().doubleValue()!=orderItem.getSkuPrice().doubleValue()){
                //商品价格不一致
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_PRICE_ERROR, null);
            }
            if(sku.getStockNum()-orderItem.getSkuNum()<0){
                //库存不足
                throw new SpzxException(ResultCodeEnum.ORDER_ITEM_PRICE_ERROR, null);
            }

        });
        //收件人信息
        Result<UserAddress> addressResult = userClient
                .getUserAddress(orderSubmitDto.getUserAddressId());
        if(addressResult.getCode()!=200){
            //用户收件人地址信息查询失败
            throw new SpzxException(ResultCodeEnum.FAIL, null);
        }
        //3、创建订单持久化
        //校验通过
        OrderInfo orderInfo = new OrderInfo();
        //订单绑定用户信息
        orderInfo.setUserId(userInfo.getId());
        orderInfo.setNickName(userInfo.getNickName());
        //订单号: 展示给用户的唯一订单编号
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        orderInfo.setOrderNo(orderNo);
        //计算订单总金额
        BigDecimal totalAmount = orderItems.stream()
                .map(orderItem -> orderItem.getSkuPrice()
                        .multiply(new BigDecimal(orderItem.getSkuNum())))
                .reduce((a, b) -> a.add(b)).get();


        orderInfo.setTotalAmount(totalAmount);
        orderInfo.setOriginalTotalAmount(totalAmount);
        //运费
        orderInfo.setFeightFee(orderSubmitDto.getFeightFee());
        //支付方式
//        orderInfo.setPayType(0);
        //订单状态：0待付款
        orderInfo.setOrderStatus(0);
        //设置收件人地址
        UserAddress userAddress = addressResult.getData();
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getFullAddress());
        //订单备注
        orderInfo.setRemark(orderSubmitDto.getRemark());
//        orderInfo.setCouponId(null);
        //orderInfo保存前 mp会通过雪花算法生成id
        this.save(orderInfo);
        Long orderId = orderInfo.getId();
        //4、保存订单的所有的订单项列表
        // 订单项关联订单id 保存
        orderItems.forEach(orderItem -> {
            orderItem.setOrderId(orderId);
        });
        orderItemService.saveBatch(orderItems);
        //5、删除购物车中已创建订单的购物项
       //发送订单创建成功的消息到kafka中： 购物车需要userId  如果还有其他的业务需要使用订单创建成功的消息 也可以携带其他的数据
       // userId  幂等性校验使用订单id   order信息
        // 远程调用： cart 删除当前用户选中的购物项
//        cartClient.delCheckedCartInfos();
       Map<String,Object> params = new HashMap<String,Object>();
       params.put("userId" , userInfo.getId());
       params.put("orderId" , orderId);
       params.put("totalAmount" , orderInfo.getTotalAmount());
       params.put("type","1");//type表示订单操作的类型  1创建订单  2取消订单  3更新订单状态
       //将对象转为json字符串
       String paramsJson = JSON.toJSONString(params);
       kafkaTemplate.send(SpzxConst.SPZX_ORDER_TOPIC ,paramsJson );
        //返回订单id
        return orderId;
    }

    @Override
    public TradeVo buy(Long skuId) {
        Result<ProductSku> result = productClient.getById(skuId);
        if(result.getCode()!=200){
            throw new SpzxException(ResultCodeEnum.FAIL , null);
        }
        ProductSku productSku = result.getData();
        Long token = IdUtil.getSnowflake(1,1).nextId();
        //将token存到redis: redis的大key问题
        stringRedisTemplate.opsForValue().set("spzx:order:"+token.toString(),"1",30, TimeUnit.MINUTES);

        OrderItem orderItem = new OrderItem();
        orderItem.setSkuNum(1);
        orderItem.setOrderId(token);
        orderItem.setSkuPrice(productSku.getSalePrice());
        orderItem.setThumbImg(productSku.getThumbImg());
        orderItem.setSkuId(skuId);
        orderItem.setSkuName(productSku.getSkuName());
        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(orderItem.getSkuPrice());
        tradeVo.setOrderItemList(Arrays.asList(orderItem));
        return tradeVo;
    }

    @Override
    public PageInfo<OrderInfo> getOrderPage(Integer pageNum, Integer pageSize,Integer orderStatus) {
        Long userId = SpzxServiceAuthInterceptor.THREAD_LOCAL.get().getId();
        //1、查询orderInfo分页数据
        Page<OrderInfo> page = new Page<>(pageNum,pageSize);
        LambdaQueryWrapper<OrderInfo> queryWrapper = Wrappers.lambdaQuery(OrderInfo.class)
                .eq(OrderInfo::getUserId, userId);

        if(orderStatus!=null){
            queryWrapper.eq(OrderInfo::getOrderStatus,orderStatus);
        }
        this.page(page, queryWrapper);
        //2、遍历orderInfo查询他的订单项列表
        page.getRecords().forEach(orderInfo -> {
            //查询每个订单的订单项列表绑定
            List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                    .eq(OrderItem::getOrderId, orderInfo.getId()));
            orderInfo.setOrderItemList(orderItems);
        });


        //3、将分页数据组装到PageInfo中
        PageInfo<OrderInfo> pageInfo = new PageInfo<>(page.getRecords());
        pageInfo.setPageNum(pageNum);
        pageInfo.setPageSize(pageSize);
        pageInfo.setTotal(page.getTotal());
        return pageInfo;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateOrderInfo(String orderNo) {
        //1、更新订单的支付状态
        //先查询订单
        OrderInfo orderInfo = this.getOne(Wrappers.lambdaQuery(OrderInfo.class)
                .eq(OrderInfo::getOrderNo, orderNo)
        );
        //更新订单
        orderInfo.setOrderStatus(1);
        orderInfo.setPayType(2);
        orderInfo.setPaymentTime(new Date());
        this.updateById(orderInfo);
        //2、更新订单涉及到的商品的销量和库存
        //查询订单所有的订单项
        List<OrderItem> orderItems = orderItemService.list(Wrappers.lambdaQuery(OrderItem.class)
                .eq(OrderItem::getOrderId, orderInfo.getId()));

        orderItems.forEach(orderItem -> {
            Long skuId = orderItem.getSkuId();
            Integer skuNum = orderItem.getSkuNum();
            //更新sku的销量和库存
            productClient.updateSalesAndStock(skuId,skuNum);
        });



    }
}
